package com.raft.core.node;

/**
 * 集群成员信息
 *
 * @see ReplicatingState
 */
class GroupMember {

    private final NodeEndpoint endpoint; // 主要是节点的服务器ip和端口
    private ReplicatingState replicatingState; // 复制进度，具体字段在日志复制部分介绍，现在可以将它简单理解为包含nextIndex和matchIndex的一个数据类
    // 复制进度默认为null，只有当节点成为Leader后，才会重置为实际的复制进度。
    private boolean major; // 成员变更中最先需要修改的是成员列表，按照规则6，节点需要一个catch-up过程。由于这个过程在节点正式加入之前，因此可以把节点加入成员列表，但是这个节点不参与选举、commitIndex计算。
	// 具体来说，就是给成员列表中的节点增加一个major属性，类型是布尔。catch-up中的节点的major=false
    private boolean removing = false;

    GroupMember(NodeEndpoint endpoint) {
        this(endpoint, null, true);
    }

    GroupMember(NodeEndpoint endpoint, ReplicatingState replicatingState, boolean major) {
        this.endpoint = endpoint;
        this.replicatingState = replicatingState;
        this.major = major;
    }

    NodeEndpoint getEndpoint() {
        return endpoint;
    }

    NodeId getId() {
        return endpoint.getId();
    }

    boolean idEquals(NodeId id) {
        return endpoint.getId().equals(id);
    }

    void setReplicatingState(ReplicatingState replicatingState) {
        this.replicatingState = replicatingState;
    }

    boolean isReplicationStateSet() {
        return replicatingState != null;
    }

    private ReplicatingState ensureReplicatingState() {
        if (replicatingState == null) {
            throw new IllegalStateException("replication state not set");
        }
        return replicatingState;
    }

    boolean isMajor() {
        return major;
    }

    void setMajor(boolean major) {
        this.major = major;
    }

    boolean isRemoving() {
        return removing;
    }

    void setRemoving() {
        removing = true;
    }

    int getNextIndex() {
        return ensureReplicatingState().getNextIndex();
    }

    int getMatchIndex() {
        return ensureReplicatingState().getMatchIndex();
    }

    /**把节点的matchIndex更新为消息中最后一条日志的索引，nextIndex更新为matchIndex+1
     * @param lastEntryIndex
     * @return
     */
    boolean advanceReplicatingState(int lastEntryIndex) {
        return ensureReplicatingState().advance(lastEntryIndex);
    }

    boolean backOffNextIndex() {
        return ensureReplicatingState().backOffNextIndex();
    }

    /**发送消息前
     */
    void replicateNow() {
        replicateAt(System.currentTimeMillis());
    }

    /**测试用
     * @param replicatedAt
     */
    void replicateAt(long replicatedAt) {
        ReplicatingState replicatingState = ensureReplicatingState();
        replicatingState.setReplicating(true);
        replicatingState.setLastReplicatedAt(replicatedAt);
    }

    boolean isReplicating() {
        return ensureReplicatingState().isReplicating();
    }

    void stopReplicating() {
        ensureReplicatingState().setReplicating(false);
    }

    /**判断是否是定时任务复制对象
     * <br/>Test if should replicate.
     * <p>
     * Return true if
     * <ol>
     * <li>not replicating</li>
     * <li>replicated but no response in specified timeout</li>
     * </ol>
     * </p>
     *
     * @param readTimeout read timeout
     * @return true if should, otherwise false
     */
    boolean shouldReplicate(long readTimeout) {
        ReplicatingState replicatingState = ensureReplicatingState();
        // 非复制状态，或者复制状态但是一个周期内没有响应，见《4变速日志复制》
        return !replicatingState.isReplicating() || System.currentTimeMillis() - replicatingState.getLastReplicatedAt() >= readTimeout;
    }

    @Override
    public String toString() {
        return "GroupMember{" +
                "endpoint=" + endpoint +
                ", major=" + major +
                ", removing=" + removing +
                ", replicatingState=" + replicatingState +
                '}';
    }

}
